当前位置: 首页 > news >正文

Apollo配置热更新

背景

系统对于首次学生认证的用户发放学生礼包权益需要发放积分礼包以及其他优惠券礼包,如果用户已经有过奖励积分记录,则无需发放积分礼包。礼包被配置成两个,一个包含积分,另外一个则不包含积分。积分的唯一标识配置在apollo之中,由于配置时将两个权益的标识配置反了导致程序没有按预期的效果执行。

并且由于更新管控比较严格,更新窗口为每周二、周四。其他时间更新属于紧急更新,需要产品与客户经理反馈更新原因,并且协调更新人员进行更新。整个链路比较长,紧急是因为一个简单配置导致应用需要重启更新,所以希望可以做到apollo能够实现热更新。

实现思路

从官网上了解到使用@ApolloConfigChangeListener可以监听到apollo的配置修改。定义apollo监听类,并且监听方法加上@ApolloConfigChangeListener即可,可以获取到修改的值、key以及修改类型。拿到这三个信息我们就可以实现热更新了。

我们可以使用观察者模式来完成系统某些配置的热更新。定义ApolloValueChangeEvent接口,通过提供不同的ApolloValueChangeEvent实现类来完成各种因配置变化而产生的一些变更。例如更新@Value注解字段的值等。

具体实现

1、定义AnoValueRefreshPostProcessor后置处理器将spring对象中的@Value注解字段与springbean的映射关系建立起来,方便后续对@Value注解字段进行热更新。

import com.xxx.xxx.xxx.log.ApiLogger; import com.xxx.framework.apollo.entity.FieldPair; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Date: 2023-11-1 16:37 */ @Component public class AnoValueRefreshPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { private Map<String, List<FieldPair>> mapper = new HashMap<>(); @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { processMetaValue(bean); return super.postProcessAfterInstantiation(bean, beanName); } /** * 这里主要的目的就是获取支持动态刷新的配置属性,然后缓存起来 * * @param bean */ private void processMetaValue(Object bean) { try { Class clz = bean.getClass(); for (Field field : clz.getDeclaredFields()) { if (field.isAnnotationPresent(Value.class)) { Value val = field.getAnnotation(Value.class); List<String> keyList = pickPropertyKey(val.value(), 0); for (String key : keyList) { mapper.computeIfAbsent(key, (k) -> new ArrayList<>()) .add(new FieldPair(bean, field, val.value())); } } } } catch (Exception e) { ApiLogger.bizLog.warn("AnoValueRefreshPostProcessor processMetaValue warn : "+e); } } /** * 实现一个基础的配置文件参数动态刷新支持 * * @param value * @return 提取key列表 */ private List<String> pickPropertyKey(String value, int begin) { int start = value.indexOf("${", begin) + 2; if (start < 2) { return new ArrayList<>(); } int middle = value.indexOf(":", start); int end = value.indexOf("}", start); String key; if (middle > 0 && middle < end) { // 包含默认值 key = value.substring(start, middle); } else { // 不包含默认值 key = value.substring(start, end); } List<String> keys = pickPropertyKey(value, end); keys.add(key); return keys; } public List<FieldPair> fieldPairs(String key){ return mapper.get(key); } }

2、定义ApolloValueChangeEvent接口

public interface ApolloValueChangeEvent{ void nodify(PropertyChangeType optType, String key, String value); }

3、定义FieldPair简化字段赋值

import com.alibaba.fastjson.JSONObject; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.core.env.Environment; import org.springframework.util.PropertyPlaceholderHelper; import java.lang.reflect.Field; /** * @Date: 2023-11-1 16:57 */ @Data @NoArgsConstructor @AllArgsConstructor public class FieldPair { private static PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}", ":", true); Object bean; Field field; String value; public void updateValue(Environment environment) { boolean access = field.isAccessible(); if (!access) { field.setAccessible(true); } String updateVal = propertyPlaceholderHelper.replacePlaceholders(value, environment::getProperty); try { if (field.getType() == String.class) { field.set(bean, updateVal); } else { field.set(bean, JSONObject.parseObject(updateVal, field.getType())); } } catch (IllegalAccessException e) { e.printStackTrace(); } field.setAccessible(access); } }

4、定义ValueRefreshEvent实现ApolloValueChangeEvent接口,用于热更新@Value字段。

package com.xxx.xxxx.apollo.event; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ulisesbocchio.jasyptspringboot.wrapper.EncryptableEnumerablePropertySourceWrapper; import com.xxx.xxx.core.log.ApiLogger; import com.xxx.xxx.core.util.SpringBeanTools; import com.xxx.framework.apollo.entity.FieldPair; import com.xxx.framework.spring.processor.AnoValueRefreshPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.List; /** * @Date: 2023-11-6 10:11 */ @Component public class ValueRefreshEvent implements ApolloValueChangeEvent{ private ConfigurableEnvironment environment; public ValueRefreshEvent(ConfigurableEnvironment environment) { this.environment = environment; } private final String name = "ApolloBootstrapPropertySources"; @Override public void nodify(PropertyChangeType optType, String key, String value) { try { EncryptableEnumerablePropertySourceWrapper compositePropertySource = (EncryptableEnumerablePropertySourceWrapper )environment.getPropertySources().get(name); compositePropertySource.refresh(); AnoValueRefreshPostProcessor anoValueRefreshPostProcessor = SpringBeanTools.getBean(AnoValueRefreshPostProcessor.class); List<FieldPair> list = anoValueRefreshPostProcessor.fieldPairs(key); if (!CollectionUtils.isEmpty(list)) { list.forEach(f -> f.updateValue(environment)); } }catch (Exception e){ ApiLogger.bizLog.info("ApolloValueChangeEventError e: "+e); } } }

5、定义Apollo监听类ApolloListener

package com.xxx.framework.apollo.listener; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import com.xxx.xxx.core.log.ApiLogger; import com.xxx.framework.apollo.event.ApolloValueChangeEvent; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.CollectionUtils; import java.util.List; import java.util.Objects; //监听阿波罗配置 @Data @Configuration public class ApolloListener { @Autowired private List<ApolloValueChangeEvent> apolloValueChangeEventList; @ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { try{ if(!CollectionUtils.isEmpty(apolloValueChangeEventList)){ changeEvent.changedKeys().stream().map(changeEvent::getChange).forEach(change -> { String changeKey = change.getPropertyName(); String newValue = change.getNewValue(); PropertyChangeType changeType = change.getChangeType(); for (ApolloValueChangeEvent apolloValueChangeEvent : apolloValueChangeEventList) { if(Objects.nonNull(apolloValueChangeEvent)){ apolloValueChangeEvent.nodify(changeType,changeKey,newValue); } } }); } }catch (Exception e){ ApiLogger.bizLog.warn("apollo listener warn : "+e); } } }

测试

1、测试接口

2、配置apollo

3、调用接口

4、修改apollo配置

5、再次调用测试接口,可以看到接口返回值已经成功变更

http://www.jsqmd.com/news/565023/

相关文章:

  • 热议口碑不错的余姚网约车专业公司 价格贵吗 - 工业设备
  • K210开发板开箱初体验:从点亮RGB灯到LCD显示,手把手带你玩转CanMV IDE
  • Electron + Vue 3 + Vite 桌面应用开发:从零到打包的实战指南
  • 腾讯混元翻译模型HY-MT1.5-1.8B:免费开源,企业级翻译解决方案
  • 如何快速扩展bootstrap-wysiwyg添加自定义命令:终极完整指南
  • 2026年职高生上本科申请机构:全周期、多元路径谁更值得信赖? - 深度智识库
  • 国产大流量蠕动泵品牌推荐:高性价比之选 - 品牌推荐大师
  • Qwen3-Reranker-0.6B一文详解:轻量0.6B参数如何实现SOTA级重排序性能
  • MT5企业级应用实战:搭建带RBAC权限的文本改写私有化服务
  • noice.nvim终极性能优化指南:让你的Neovim编辑器运行如飞
  • 终极指南:如何在Jetpack Compose中完美集成Alerter通知库
  • IDEA插件开发:集成Nunchaku-flux-1-dev实现代码注释自动图解
  • 免费降AI率工具怎么选?2026年实测3款高性价比工具 - 老米_专讲AIGC率
  • 告别复制粘贴!用Qwen Code在终端里直接重构500行烂代码(附真实项目截图)
  • Comsol谷霍尔光子晶体(VPC)-片上通信的太赫兹拓扑光子学 本模型复现文章
  • 如何通过InstantClick事件回调实现精准的性能监控:开发者必备指南
  • zynq7020 u-boot 外设配置实战指南
  • 终极指南:chaiNNer 200+图像处理节点库完全解析与实战应用
  • PyTorch 3.0静训性能断崖预警:当AllReduce延迟>8.3ms或图编译耗时>117s时,你的训练任务已在 silently fail——附实时诊断CLI工具
  • 2026年艺体生上本科申请机构:升学规划全攻略,多元路径助你圆梦本科 - 深度智识库
  • faster-whisper-GUI效率优化实战指南:3大策略提升开源工具使用效率
  • 新手福音:用快马平台将vmware官网概念转化为可交互的虚拟机演示代码
  • Zabbix 监控多线路出口IP并通过飞书告警完整教程
  • eNSP安装避坑指南:WinPcap/Wireshark/VirtualBox依赖关系解析
  • uniapp集成腾讯地图:从marker点聚合到轨迹回放的跨端实战与性能调优
  • 北京工程瓷砖批发靠谱品牌有哪些 - myqiye
  • Godep历史意义揭秘:Go依赖管理工具的开创者如何改变开发方式
  • SD-Trainer:3步打造专属AI绘画模型的终极指南
  • FPGA密码锁设计避坑指南:状态机划分、时序约束与安全逻辑的那些事儿
  • Leela Zero性能调优终极指南:CPU与GPU配置的黄金法则